/**
 *******************************************************************************
 * @file  i2c/i2c_master_polling_fifo/source/main.c
 * @brief Main program of I2C master polling fifo for the Device Driver Library.
 @verbatim
   Change Logs:
   Date             Author          Notes
   2023-05-31       CDT             First version
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2022-2023, Xiaohua Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by XHSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 *
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "main.h"

/**
 * @addtogroup HC32F448_DDL_Examples
 * @{
 */

/**
 * @addtogroup I2C_master_polling_fifo
 * @{
 */

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
/* unlock/lock peripheral */
#define EXAMPLE_PERIPH_WE               (LL_PERIPH_GPIO | LL_PERIPH_EFM | LL_PERIPH_FCG | \
                                         LL_PERIPH_PWC_CLK_RMU | LL_PERIPH_SRAM)
#define EXAMPLE_PERIPH_WP               (LL_PERIPH_EFM | LL_PERIPH_FCG | LL_PERIPH_SRAM)

/* Define I2C unit used for the example */
#define I2C_UNIT                        (CM_I2C1)
#define I2C_FCG_USE                     (FCG1_PERIPH_I2C1)
/* Define slave device address for example */
#define DEVICE_ADDR                     (0x06U)
/* I2C address mode */
#define I2C_ADDR_MD_7BIT                (0U)
#define I2C_ADDR_MD_10BIT               (1U)
/* Config I2C address mode: I2C_ADDR_MD_7BIT or I2C_ADDR_MD_10BIT */
#define I2C_ADDR_MD                     (I2C_ADDR_MD_7BIT)

/* Define port and pin for SDA and SCL */
#define I2C_SCL_PORT                    (GPIO_PORT_E)
#define I2C_SCL_PIN                     (GPIO_PIN_01)
#define I2C_SDA_PORT                    (GPIO_PORT_E)
#define I2C_SDA_PIN                     (GPIO_PIN_00)
#define I2C_GPIO_SCL_FUNC               (GPIO_FUNC_49)
#define I2C_GPIO_SDA_FUNC               (GPIO_FUNC_48)

#define TIMEOUT                         (0x40000UL)

/* Define Write and read data length for the example */
#define TEST_DATA_LEN                   (256U)
/* Define i2c baudrate */
#define I2C_BAUDRATE                    (400000UL)

#define LED_GREEN_PORT                  (GPIO_PORT_A)
#define LED_GREEN_PIN                   (GPIO_PIN_02)

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/
static uint8_t u8TxBuf[TEST_DATA_LEN];
static uint8_t u8RxBuf[TEST_DATA_LEN];

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/

static void LedInit(void)
{
    stc_gpio_init_t stcGpioInit;

    /* RGB LED initialize */
    (void)GPIO_StructInit(&stcGpioInit);
    (void)GPIO_Init(LED_GREEN_PORT, LED_GREEN_PIN, &stcGpioInit);

    /* "Turn off" LED before set to output */
    GPIO_SetPins(LED_GREEN_PORT, LED_GREEN_PIN);

    /* Output enable */
    GPIO_OutputCmd(LED_GREEN_PORT, LED_GREEN_PIN, ENABLE);
}

static void LedToggle(void)
{
    GPIO_TogglePins(LED_GREEN_PORT, LED_GREEN_PIN);
}

/**
 * @brief  SysTick interrupt handler function.
 * @param  None
 * @retval None
 */
void SysTick_Handler(void)
{
    SysTick_IncTick();

    __DSB();  /* Arm Errata 838869 */
}

/**
 * @brief I2Cx send data in fifo
 * @param [in] I2Cx                 Pointer to I2C instance register base.
 *                                  This parameter can be a value of the following:
 *         @arg CM_I2C or CM_I2Cx:  I2C instance register base.
 * @param [in] au8TxData            The data array to be sent
 * @param [in] u32Size              Number of data in array pau8TxData
 * @param [in] u32Timeout           Maximum count of trying to get a status of a flag in status register
 * @retval int32_t
 *         - LL_OK:                 Success
 *         - LL_ERR_TIMEOUT:        Failed
 *         - LL_ERR_INVD_PARAM:     Parameter error
 */
int32_t I2C_TransData_FIFO(CM_I2C_TypeDef *I2Cx, uint8_t const au8TxData[], uint32_t u32Size, uint32_t u32Timeout)
{
    int32_t i32Ret = LL_OK;
    __IO uint32_t u32Count = 0UL;

    if (au8TxData != NULL) {
        while ((u32Count != u32Size) && (i32Ret == LL_OK)) {
            /* Wait tx buffer empty */
            i32Ret = I2C_WaitStatus(I2Cx, I2C_FIFO_FLAG_TFFULL, RESET, u32Timeout);

            if (i32Ret == LL_OK) {
                /* Send one byte data */
                I2C_WriteData(I2Cx, au8TxData[u32Count]);
                u32Count++;
            } else {
                break;
            }
            /* If receive NACK*/
            if (I2C_GetStatus(I2Cx, I2C_FLAG_NACKF) == SET) {
                break;
            }
        }
    } else {
        i32Ret = LL_ERR_INVD_PARAM;
    }
    /* Wait transfer end*/
    i32Ret = I2C_WaitStatus(I2Cx, I2C_FLAG_TX_CPLT, SET, u32Timeout);
    return i32Ret;
}

/**
 * @brief  Master transmit data
 *
 * @param  [in] u16DevAddr          The slave address
 * @param  [in] au8Data             The data array
 * @param  [in] u32Size             Data size
 * @param  [in] u32Timeout          Time out count
 * @retval int32_t:
 *            - LL_OK:              Success
 *            - LL_ERR_TIMEOUT:     Time out
 */
static int32_t I2C_Master_Transmit(uint16_t u16DevAddr, uint8_t const au8Data[], uint32_t u32Size, uint32_t u32Timeout)
{
    int32_t i32Ret;

    I2C_Cmd(I2C_UNIT, ENABLE);
    I2C_SWResetCmd(I2C_UNIT, ENABLE);
    I2C_SWResetCmd(I2C_UNIT, DISABLE);
    I2C_FIFO_TxFlush(I2C_UNIT);

    i32Ret = I2C_Start(I2C_UNIT, u32Timeout);
    if (LL_OK == i32Ret) {
#if (I2C_ADDR_MD == I2C_ADDR_MD_10BIT)
        i32Ret = I2C_Trans10BitAddr(I2C_UNIT, u16DevAddr, I2C_DIR_TX, u32Timeout);
#else
        i32Ret = I2C_TransAddr(I2C_UNIT, u16DevAddr, I2C_DIR_TX, u32Timeout);
#endif

        if (LL_OK == i32Ret) {
            i32Ret = I2C_TransData_FIFO(I2C_UNIT, au8Data, u32Size, u32Timeout);
        }
    }

    (void)I2C_Stop(I2C_UNIT, u32Timeout);
    I2C_Cmd(I2C_UNIT, DISABLE);

    return i32Ret;
}

/**
 * @brief I2Cx receive data and stop in fifo(for master)
 * @param [in] I2Cx                 Pointer to I2C instance register base.
 *                                  This parameter can be a value of the following:
 *        @arg CM_I2C or CM_I2Cx:   I2C instance register base.
 * @param [out] au8RxData           Array to hold the received data
 * @param [in] u32Size              Number of data to be received
 * @param [in] u32Timeout           Maximum count of trying to get a status of a flag in status register
 * @retval int32_t
 *         - LL_OK:                 Success
 *         - LL_ERR_TIMEOUT:        Failed
 *         - LL_ERR_INVD_PARAM:     Parameter error
 */
int32_t I2C_MasterReceiveDataAndStop_FIFO(CM_I2C_TypeDef *I2Cx, uint8_t au8RxData[], uint32_t u32Size, uint32_t u32Timeout)
{
    int32_t i32Ret = LL_OK;
    uint32_t i;

    if (au8RxData != NULL) {
        uint32_t u32FastAckDis = READ_REG32_BIT(I2Cx->CR3, I2C_CR3_FACKEN);

        for (i = 0UL; i < u32Size; i++) {
            /*wait for rx_fifo not empty*/
            i32Ret = I2C_WaitStatus(I2Cx, I2C_FIFO_FLAG_RFREQ, SET, u32Timeout);

            if (0UL == u32FastAckDis) {
                if ((u32Size >= 2UL) && (i == (u32Size - 2UL))) {
                    I2C_AckConfig(I2Cx, I2C_NACK);
                }
            } else {
                if (i != (u32Size - 1UL)) {
                    I2C_AckConfig(I2Cx, I2C_ACK);
                } else {
                    I2C_AckConfig(I2Cx, I2C_NACK);
                }
            }

            if (i32Ret == LL_OK) {
                /* Stop before read last data */
                if (i == (u32Size - 1UL)) {
                    I2C_ClearStatus(I2Cx, I2C_FLAG_STOP);
                    I2C_GenerateStop(I2Cx);
                }
                /* read data from register */
                au8RxData[i] = I2C_ReadData(I2Cx);

                if (i == (u32Size - 1UL)) {
                    /* Wait stop flag after DRR read */
                    i32Ret = I2C_WaitStatus(I2Cx, I2C_FLAG_STOP, SET, u32Timeout);
                }
            } else {
                break;
            }
        }
        I2C_AckConfig(I2Cx, I2C_ACK);
    } else {
        i32Ret = LL_ERR_INVD_PARAM;
    }

    return i32Ret;
}

/**
 * @brief  Master receive data
 *
 * @param  [in] u16DevAddr          The slave address
 * @param  [in] au8Data             The data array
 * @param  [in] u32Size             Data size
 * @param  [in] u32Timeout          Time out count
 * @retval int32_t:
 *            - LL_OK:              Success
 *            - LL_ERR_TIMEOUT:     Time out
 */
static int32_t I2C_Master_Receive(uint16_t u16DevAddr, uint8_t au8Data[], uint32_t u32Size, uint32_t u32Timeout)
{
    int32_t i32Ret;

    I2C_Cmd(I2C_UNIT, ENABLE);
    I2C_SWResetCmd(I2C_UNIT, ENABLE);
    I2C_SWResetCmd(I2C_UNIT, DISABLE);
    I2C_FIFO_RxFlush(I2C_UNIT);

    i32Ret = I2C_Start(I2C_UNIT, u32Timeout);
    if (LL_OK == i32Ret) {
        if (1UL == u32Size) {
            I2C_AckConfig(I2C_UNIT, I2C_NACK);
        }

#if (I2C_ADDR_MD == I2C_ADDR_MD_10BIT)
        i32Ret = I2C_Trans10BitAddr(I2C_UNIT, u16DevAddr, I2C_DIR_RX, u32Timeout);
#else
        i32Ret = I2C_TransAddr(I2C_UNIT, u16DevAddr, I2C_DIR_RX, u32Timeout);
#endif

        if (LL_OK == i32Ret) {
            i32Ret = I2C_MasterReceiveDataAndStop_FIFO(I2C_UNIT, au8Data, u32Size, u32Timeout);
        }

        I2C_AckConfig(I2C_UNIT, I2C_ACK);
    }

    if (LL_OK != i32Ret) {
        (void)I2C_Stop(I2C_UNIT, u32Timeout);
    }
    I2C_Cmd(I2C_UNIT, DISABLE);
    return i32Ret;
}

/**
 * @brief  Initialize the I2C peripheral for master
 * @param  None
 * @retval int32_t:
 *            - LL_OK:                  Success
 *            - LL_ERR_INVD_PARAM:      Invalid parameter
 */
static int32_t Master_Initialize(void)
{
    int32_t i32Ret;
    stc_i2c_init_t stcI2cInit;
    float32_t fErr;

    I2C_DeInit(I2C_UNIT);

    (void)I2C_StructInit(&stcI2cInit);
    stcI2cInit.u32ClockDiv = I2C_CLK_DIV1;
    stcI2cInit.u32Baudrate = I2C_BAUDRATE;
    stcI2cInit.u32SclTime = 33UL;
    i32Ret = I2C_Init(I2C_UNIT, &stcI2cInit, &fErr);

    I2C_FIFO_Cmd(I2C_UNIT, ENABLE);
    return i32Ret;
}

/**
 * @brief  Main function
 * @param  None
 * @retval int32_t return value, if needed
 */
int32_t  main(void)
{
    uint32_t i;

    /* Unlock peripherals or registers */
    LL_PERIPH_WE(EXAMPLE_PERIPH_WE);
    /* BSP initialization */
    BSP_CLK_Init();
    BSP_KEY_Init();
    LedInit();
    /* SysTick configuration */
    (void)SysTick_Init(1000U);

    for (i = 0UL; i < TEST_DATA_LEN; i++) {
        u8TxBuf[i] = (uint8_t)(i + 1U);
    }
    (void)memset(u8RxBuf, (int32_t)0x00U, TEST_DATA_LEN);

    /* Initialize I2C port*/
    GPIO_SetFunc(I2C_SCL_PORT, I2C_SCL_PIN, I2C_GPIO_SCL_FUNC);
    GPIO_SetFunc(I2C_SDA_PORT, I2C_SDA_PIN, I2C_GPIO_SDA_FUNC);

    /* Enable I2C Peripheral*/
    FCG_Fcg1PeriphClockCmd(I2C_FCG_USE, ENABLE);
    /* Initialize I2C peripheral and enable function*/
    if (LL_OK != Master_Initialize()) {
        /* Peripheral registers write protected */
        LL_PERIPH_WP(EXAMPLE_PERIPH_WP);
        /* Initialize error*/
        for (;;) {
            LedToggle();
            SysTick_Delay(200UL);
        }
    }
    /* Peripheral registers write protected */
    LL_PERIPH_WP(EXAMPLE_PERIPH_WP);

    /* Wait key */
    while (RESET == BSP_KEY_GetStatus(BSP_KEY_1)) {
        ;
    }

    SysTick_Delay(5UL);

    (void)I2C_Master_Transmit(DEVICE_ADDR, u8TxBuf, TEST_DATA_LEN, TIMEOUT);

    /* 50mS delay for device*/
    SysTick_Delay(50UL);

    (void)I2C_Master_Receive(DEVICE_ADDR, u8RxBuf, TEST_DATA_LEN, TIMEOUT);

    /* Compare the data */
    for (i = 0UL; i < TEST_DATA_LEN; i++) {
        if (u8TxBuf[i] != u8RxBuf[i]) {
            /* Data write error*/
            for (;;) {
                LedToggle();
                SysTick_Delay(200UL);
            }
        }
    }

    /* I2C master polling comunication success */
    for (;;) {
        LedToggle();
        SysTick_Delay(1000UL);
    }
}

/**
 * @}
 */

/**
 * @}
 */

/*******************************************************************************
 * EOF (not truncated)
 ******************************************************************************/
